/*
 * This program is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 3 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 * for more details.
 * 
 * You should have received a copy of the GNU General Public License along 
 * with this program; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <QFileDialog>
#include <QSettings>
#include <QMessageBox>
#include <QStandardItemModel>
#include <QStandardItem>
#include <QPixmapCache>
#include <QKeyEvent>
#include <QComboBox>
#include "mainwindow.h"
#include "postedit.h"
#include "ui_postedit.h"
#include "chanmanager.h"
#include "components/chan.h"
#include "components/thread.h"
#include "components/board.h"
#include "log.h"
#include "text.h"
#include "threadoverviewdelegate.h"
#include "imageeditor/imageeditor.h"
#include "spellcheck/spellcheckermanager.h"
#include "spellcheck/spellchecker.h"
#include "spellcheckhighlighter.h"
#include "featuresdialog.h"
#include "imagestorage.h"
#include "coding/codingmanager.h"
#include "watermarking/watermarkgeneratorfactory.h"

extern Wishmaster::ImageStorage* g_imagestorage;

using Wishmaster::ThreadId;

PostEdit::PostEdit(Wishmaster::ChanManager* chanManager, const Wishmaster::ThreadId& threadId, QWidget *parent) :
		QDialog(parent),
		m_ui(new Ui::PostEdit),
		m_chanManager(chanManager),
		m_threadId(threadId),
		m_threadOverviewVisible(true),
		m_imageEditor(0)
{
	Wishmaster::Chan* chan = m_chanManager->chanlist()->getChan(threadId.chanName());
	m_constraints = chan->constraints(threadId.boardId());

	m_ui->setupUi(this);
	initUi();

	refreshCaptcha();

	connect(chan, SIGNAL(postingError(QString)), this, SLOT(postingError(QString)));
	connect(chan, SIGNAL(postingDone(ThreadId)), this, SLOT(accept()));

	connect(chanManager, SIGNAL(captcha(ThreadId,QByteArray)), this, SLOT(captcha(ThreadId,QByteArray)));

	m_eventLock = false;

	m_keymap = chan->keymap();
	QSettings settings;
	if(settings.value("general/useKeymaps", true).toBool())
	{
		if(m_keymap)
			m_ui->e_captcha->installEventFilter(this);
	}

	fillAutoAttachments();
}

PostEdit::~PostEdit()
{
	if(m_imageEditor)
		delete m_imageEditor;
	delete m_highlighter;
	QSettings settings;
	settings.setValue("posting/details", m_ui->b_hideDetails->isChecked());
	settings.setValue("posting/sage_checkbox", m_ui->cb_sage->isChecked());
	settings.setValue("posting/email", m_ui->e_email->text());
	delete m_ui;
}

void PostEdit::initUi()
{
	Wishmaster::Chan* chan = m_chanManager->chanlist()->getChan(m_threadId.chanName());
	// Load saved ui state
	QSettings settings;
	m_ui->e_password->setText(settings.value("posting/defaultPassword", "").toString());
	m_ui->b_hideDetails->setChecked(settings.value("posting/details").toBool());
	setDetailsVisibility(settings.value("posting/details").toBool());
	m_ui->cb_sage->setChecked(settings.value("posting/sage_checkbox", false).toBool());
	m_ui->e_email->setText(settings.value("posting/email", QString("")).toString());

	m_ui->lv_posts->setItemDelegate(new ThreadOverviewDelegate());
	m_ui->lv_posts->setModel(new QStandardItemModel());

	m_ui->gv_captcha->setScene(&m_scene);

	// This call will set proper enabled/disabled state off 'add/delete image' buttons
	attachSelectionChanged();

	// Enable or disable captcha field, depending on captcha presence on particular chan
	if((m_threadId.threadNumericId() != 0) && (chan->property("nopostcaptcha").toBool()))
	{
		m_ui->gv_captcha->setEnabled(false);
		m_ui->e_captcha->setEnabled(false);
	}
	else
	{
		m_ui->gv_captcha->setEnabled(true);
		m_ui->e_captcha->setEnabled(true);
	}

	m_ui->te_postText->setFocus(Qt::OtherFocusReason);
	m_ui->te_postText->moveCursor(QTextCursor::End);

	m_highlighter = new SpellCheckHighlighter(m_ui->te_postText);
	m_highlighter->setSpellChecker(Wishmaster::SpellCheckerManager::instance()->defaultSpellChecker());

	MainWindow* mainwindow = static_cast<MainWindow*>(parent());
	ImageManager* imagemanager = mainwindow->getImageManager();
	connect(imagemanager, SIGNAL(pictureSelected(QString)), this, SLOT(pictureSelected(QString)));

	updatePicLimits();
	updateMessages();
	updateGui();
	m_ratings = chan->agent()->contentRatings();
	if(m_ratings.size() > 0)
	{
		m_ui->tw_attachments->setColumnCount(2);
		m_ui->tw_attachments->setHeaderHidden(false);
		QStringList labels;
		labels.append(tr("Filename"));
		labels.append(tr("Rating"));
		m_ui->tw_attachments->setHeaderLabels(labels);
	}
}

void PostEdit::timerEvent(QTimerEvent* /*e*/)
{
	Wishmaster::Chan* chan = m_chanManager->chanlist()->getChan(m_threadId.chanName());
	if((m_threadId.threadNumericId() != 0) && (chan->property("nopostcaptcha").toBool()))
		return;
	chan->agent()->getCaptcha(m_threadId);
	killTimer(m_timerID);
	m_timerID = 0;
}

void PostEdit::changeEvent(QEvent *e)
{
	QDialog::changeEvent(e);
	switch (e->type()) {
	case QEvent::LanguageChange:
		m_ui->retranslateUi(this);
		break;
	default:
		break;
	}
}

void PostEdit::captcha(const Wishmaster::ThreadId& threadId, const QByteArray& p)
{
	if(threadId != m_threadId)
		return;
	m_captcha.loadFromData(p);
	if(m_captcha.size().width() > m_ui->gv_captcha->width())
	{
		m_ui->gv_captcha->setMinimumWidth(m_captcha.size().width() + 32);
	}
	if(m_captcha.size().height() > m_ui->gv_captcha->height())
	{
		m_ui->gv_captcha->setMinimumHeight(m_captcha.size().height() + 32);
	}
	m_ui->gv_captcha->scene()->clear();
	m_ui->gv_captcha->scene()->addPixmap(m_captcha);
}

void PostEdit::preprocessPost(Wishmaster::PostDescriptor& post)
{
	QList<ExtraInfo> extras;
	QSettings settings;
	QString sign = settings.value("postfeatures/signature").toString();
	bool watermarkSign = settings.value("postfeatures/injectsignature").toBool();
	if(!sign.isEmpty())
	{
		if(!watermarkSign)
			post.text += ("\n" + sign);
		else
		{
			ExtraInfo signinfo;
			signinfo.id = "signature";
			signinfo.data = sign.toUtf8();
			extras.append(signinfo);
		}
	}
	int watermarkMethod = settings.value("postfeatures/watermarking").toUInt();
	if(watermarkMethod)
	{
		WatermarkGeneratorPtr gen;
		switch(watermarkMethod)
		{
			case FeaturesDialog::WatermarkWhitespace:
				gen = WatermarkGeneratorFactory::instance()->createTextWatermark("whitespace");
				break;
			default:
				break;
		}
		if(gen)
		{
			if(gen->type() == WatermarkGenerator::tText)
			{
				TextWatermarkGeneratorPtr textgen = gen.dynamicCast<TextWatermarkGenerator>();
				QByteArray watermark = CodingManager::instance()->store(extras);
				post.text = textgen->cast(post.text, watermark);
			}
		}
	}
}

void PostEdit::send()
{
	Wishmaster::PostDescriptor post;
	post.board = m_threadId.boardId();
	post.parent_thread = m_threadId;
	post.name = m_ui->e_name->text();
	post.email = m_ui->e_email->text();
	post.topic = m_ui->e_topic->text();
	post.text = m_ui->te_postText->toPlainText();
	post.sage = m_ui->cb_sage->isChecked();
	addAttachments(&post);
	post.password = m_ui->e_password->text();

	preprocessPost(post);

	if(m_threadId.threadNumericId())
	{
		m_chanManager->addPost(m_threadId, post, m_ui->e_captcha->text());
	}
	else
	{
		m_chanManager->createThread(m_threadId.chanName(), m_threadId.boardId(), post, m_ui->e_captcha->text());
	}
	QSettings settings;
	settings.setValue("posting/defaultPassword", m_ui->e_password->text());
	QDialog::accept();
}

void PostEdit::browse()
{
	QString filename = QFileDialog::getOpenFileName(this, tr("Select file to upload"), "", tr("Images (*.jpg *.png *.gif);;All files (*.*)"));
	if(!filename.isEmpty())
	{
		pictureSelected(filename);
	}
}

void PostEdit::selectFromImageManager()
{
	if(!parent()->inherits("MainWindow"))
	{
		log(llDebug, "Invalid parent of PostEdit");
		return;
	}
	MainWindow* mainwindow = static_cast<MainWindow*>(parent());
	ImageManager* imagemanager = mainwindow->getImageManager();
	imagemanager->setMode(true);
	imagemanager->show();
}

void PostEdit::pictureSelected(const QString& path)
{
	QTreeWidgetItem* selectedItem = 0;
	int topitems = m_ui->tw_attachments->topLevelItemCount();
	if(m_ui->tw_attachments->selectedItems().count() > 0)
		selectedItem = m_ui->tw_attachments->selectedItems().at(0);
	int index = -1;
	if(selectedItem)
		index = m_ui->tw_attachments->indexOfTopLevelItem(selectedItem);

	// TODO check for maximum
	if(index >= 0)
	{
		Wishmaster::ExtraInfoDescriptor extra;
		extra.filename = path;
		QFile f(extra.filename);
		if(f.open(QIODevice::ReadOnly))
		{
			extra.info = f.readAll();
			f.close();
			m_attachments[index].extras.append(extra);
		}
	}
	else
	{
		bool piclimit;
		if(m_threadId.threadNumericId())
		{
			piclimit = (topitems >= m_constraints.postPicsMax) && (m_constraints.postPicsMax != -1);
		}
		else
		{
			piclimit = (topitems >= m_constraints.oppostPicsMax) && (m_constraints.postPicsMax != -1);
		}
		if(piclimit)
		{
			QMessageBox::critical(this, tr("Error"), tr("Maximum numbers of images for this chan is reached"));
			return;
		}
		Wishmaster::AttachmentDescriptor attach;
		attach.filename = path;
		if(m_ratings.size() > 0)
			attach.rating = m_ratings.at(0);
		appendAddendum(attach);
		m_attachments.append(attach);
	}
	updateAttachments();

	updatePicLimits();
	updateMessages();
}

void PostEdit::fillAutoAttachments()
{
	QSettings settings;
	int autopicture = settings.value("postfeatures/autopicture").toUInt();
	if(autopicture != FeaturesDialog::AutopicDisable)
	{
		m_attachments.clear();
		if(autopicture == FeaturesDialog::AutopicFromFs || autopicture == FeaturesDialog::AutopicFromDb)
		{
			Wishmaster::AttachmentDescriptor attach;
			attach.filename = settings.value("postfeatures/filename").toString();
			appendAddendum(attach);
			m_attachments.append(attach);
		}
		else if(autopicture == FeaturesDialog::AutopicFromDbRandom)
		{
			QStringList tags = settings.value("postfeatures/tags").toString().split(' ');
			QList<int> ids = g_imagestorage->getIDsForTags(tags);
			if(!ids.isEmpty())
			{
				int id = ids.at(qrand() % ids.size());
				Wishmaster::AttachmentDescriptor attach;
				attach.filename = g_imagestorage->getPathForID(id);
				appendAddendum(attach);
				m_attachments.append(attach);
			}
		}
	}
	updateAttachments();
}

void PostEdit::appendAddendum(Wishmaster::AttachmentDescriptor& attach)
{
	// Look, if addendum is already added
	foreach(const Wishmaster::ExtraInfoDescriptor& extra, attach.extras)
	{
		if(extra.filename == "Addendum")
			return;
	}
	QSettings settings;
	if(settings.value("postfeatures/randomaddendum").toBool())
	{
		Wishmaster::ExtraInfoDescriptor extra;
		extra.filename = "Addendum";
		QByteArray addendum;
		for(int i = 0; i < 4; i++)
		{
			addendum += QByteArray::number(qrand());
		}
		extra.info = addendum;
		attach.extras.append(extra);
	}
}

void PostEdit::refreshCaptcha()
{
	m_timerID = startTimer(100);
}

void PostEdit::postingError(const QString& errortype)
{
	if(errortype.isEmpty())
	{
		QDialog::accept();
		return;
	}
	QMessageBox::warning(this, tr("Error"), errortype);
}

void PostEdit::setText(const QString& txt)
{
	m_ui->te_postText->setPlainText(txt);
}

bool PostEdit::eventFilter(QObject* obj, QEvent* event)
{
	// Keymap translation
	if(obj == m_ui->e_captcha && !m_eventLock)
	{
		if(event->type() == QEvent::KeyPress)
		{
			QKeyEvent* ev = static_cast<QKeyEvent*>(event);
			if(!ev->text().isEmpty())
			{
				QString str = m_keymap->translate(ev->text().at(0));
				QKeyEvent e(QEvent::KeyPress, ev->key(), ev->modifiers(), str, ev->isAutoRepeat(), ev->count());
				m_eventLock = true;
				QApplication::sendEvent(m_ui->e_captcha, &e);
				m_eventLock = false;
				return true;
			}
		}
	}
	return QDialog::eventFilter(obj, event);
}

void PostEdit::setDetailsVisibility(bool v)
{
	m_ui->l_name->setVisible(v);
	m_ui->e_name->setVisible(v);
	m_ui->l_email->setVisible(v);
	m_ui->e_email->setVisible(v);
	m_ui->l_topic->setVisible(v);
	m_ui->e_topic->setVisible(v);
	m_ui->l_password->setVisible(v);
	m_ui->e_password->setVisible(v);
	if(m_threadOverviewVisible)
		m_ui->gb_threadOverview->setVisible(v);
	updateGui();
}

void PostEdit::updateThreadOverview()
{
	QStandardItemModel* model = qobject_cast<QStandardItemModel*>(m_ui->lv_posts->model());
	Q_ASSERT(model);

	model->clear();
	model->setColumnCount(1);
	Wishmaster::Chan* chan = m_chanManager->chanlist()->getChan(m_threadId.chanName());
	Wishmaster::ThreadPtr thread = chan->board(m_threadId.boardId())->getThread(m_threadId);
	if(!thread)
		return;

	for(int postNumber = 0; postNumber < thread->getActualPostsCount(); postNumber++)
	{
		PostPtr post = thread->getPost(postNumber);
		QString txt = post->text();
		txt = htmlToPlainText(txt);
		QStandardItem* item = new QStandardItem(txt);
		item->setData((unsigned int)post->id().postId(), PostIdRole);
		if(post->attachmentCount() > 0)
		{
			QImage img = post->attachment(0)->thumbnail();
			if(!img.isNull())
				item->setIcon(QPixmap::fromImage(img));
		}
		model->appendRow(item);
	}
}

void PostEdit::threadOverviewClicked(const QModelIndex& index)
{
	m_ui->te_postText->append(QString(">>").append(index.data(PostIdRole).toString())); // FIXME this should be obtained from boardparser
}

void PostEdit::setThreadOverviewVisible(bool v)
{
	m_threadOverviewVisible = v;
	m_ui->gb_threadOverview->setVisible(v);
}

void PostEdit::editImage()
{
	m_imageEditor = new ImageEditor(this);
	connect(m_imageEditor, SIGNAL(imageReady(QSharedPointer<QTemporaryFile>)), this, SLOT(imageReady(QSharedPointer<QTemporaryFile>)));
	QTreeWidgetItem* selectedItem = 0;
	QImage image;
	if(m_ui->tw_attachments->selectedItems().count() > 0)
	{
		selectedItem = m_ui->tw_attachments->selectedItems().at(0);
		if(!image.load(selectedItem->text(0)))
		{
			QMessageBox::warning(this, tr("Error"), tr("Unable to open selected image"));
			return;
		}
	}
	if(!image.isNull())
		m_imageEditor->setImage(image);
	else
		m_imageEditor->newImage();
	m_imageEditor->show();
}

void PostEdit::imageReady(QSharedPointer<QTemporaryFile> file)
{
	m_tempfiles.append(file);
	log(llDebug, "Image: %s", qPrintable(file->fileName()));
	QTreeWidgetItem* selectedItem = 0;
	bool addNew = true;
	int index = 0;
	if(m_ui->tw_attachments->selectedItems().count() > 0)
	{
		selectedItem = m_ui->tw_attachments->selectedItems().at(0);
		index = m_ui->tw_attachments->indexOfTopLevelItem(selectedItem);
		if(index >= 0)
		{
			addNew = false;
			selectedItem->setText(0, file->fileName());
		}
	}
	file->reset();
	if(addNew)
	{
		QTreeWidgetItem* item = new QTreeWidgetItem();
		item->setText(0, file->fileName());
		m_ui->tw_attachments->addTopLevelItem(item);
		Wishmaster::AttachmentDescriptor attach;
		attach.filename = file->fileName();
		attach.data = file->readAll();
		attach.useData = true;
		m_attachments.append(attach);
	}
	else
	{
		if((index >= 0) && (index < m_attachments.size()))
		{
			m_attachments[index].useData = true;
			m_attachments[index].filename = file->fileName();
			m_attachments[index].data = file->readAll();
			log(llDebug, "PostEdit::imageReady: data size: %d", m_attachments[index].data.size());
		}
		else
		{
			log(llDebug, "PostEdit::imageReady: invalid index");
		}
	}
}

void PostEdit::addAttachments(Wishmaster::PostDescriptor* desc)
{
	for(int i = 0; i < m_attachments.size(); i++)
	{
		Wishmaster::AttachmentDescriptor& attach = m_attachments[i];
		QTreeWidgetItem* item = m_ui->tw_attachments->topLevelItem(i);
		QComboBox* box = qobject_cast<QComboBox*>(m_ui->tw_attachments->itemWidget(item, 1));
		if(box)
		{
			attach.rating = box->currentText();
		}
		desc->attachments.append(attach);
	}
}

void PostEdit::attachSelectionChanged()
{
	m_ui->b_delete->setEnabled(m_ui->tw_attachments->selectedItems().count() > 0);
}

void PostEdit::attachDelete()
{
	if(m_ui->tw_attachments->selectedItems().count() == 0)
		return;
	int index =  m_ui->tw_attachments->indexOfTopLevelItem(m_ui->tw_attachments->selectedItems().at(0));
	if(index < 0)
	{
		QTreeWidgetItem* parent = m_ui->tw_attachments->selectedItems().at(0)->parent();
		index = m_ui->tw_attachments->indexOfTopLevelItem(parent);
		int subindex = parent->indexOfChild(m_ui->tw_attachments->selectedItems().at(0));
		m_attachments[index].extras.removeAt(subindex);
	}
	else
	{
		m_attachments.removeAt(m_ui->tw_attachments->indexOfTopLevelItem(m_ui->tw_attachments->selectedItems().at(0)));
	}
	updateAttachments();
}

void PostEdit::textChanged()
{
	Wishmaster::Chan* chan = m_chanManager->chanlist()->getChan(m_threadId.chanName());
	if(!chan)
		return;
	int charlimit = m_constraints.charsMax;
	if(charlimit > 0 && m_ui->te_postText->toPlainText().length() > charlimit)
		m_charLimit = true;
	else
		m_charLimit = false;

	updatePicLimits();
	updateMessages();
}

void PostEdit::updateMessages()
{
	QString result;
	if(m_charLimit)
		result += tr("Character limit is reached<br />");
	if(m_picLowLimit)
		result += tr("This post should contain more images<br />");
	m_ui->l_messages->setText(result);
}

void PostEdit::updatePicLimits()
{
	if(m_threadId.threadNumericId())
		m_picLowLimit = m_ui->tw_attachments->topLevelItemCount() < m_constraints.postPicsMin;
	else
		m_picLowLimit = m_ui->tw_attachments->topLevelItemCount() < m_constraints.oppostPicsMin;
}

void PostEdit::updateGui()
{
	m_ui->e_email->setVisible(m_constraints.emailField);
	m_ui->l_email->setVisible(m_constraints.emailField);
	m_ui->cb_sage->setVisible(m_constraints.sageCheckbox);
}

void PostEdit::updateAttachments()
{
	m_ui->tw_attachments->clear();
	foreach(const Wishmaster::AttachmentDescriptor& attach, m_attachments)
	{
		QTreeWidgetItem* it = new QTreeWidgetItem(m_ui->tw_attachments);
		it->setText(0, attach.filename);
		if(m_ratings.size() > 0)
		{
			QComboBox* box = new QComboBox();
			box->addItems(m_ratings);
			m_ui->tw_attachments->setItemWidget(it, 1, box);
			box->setCurrentIndex(box->findText(attach.rating));
		}
		it->setIcon(0, pixmapForFile(attach.filename));
		foreach(const Wishmaster::ExtraInfoDescriptor& extra, attach.extras)
		{
			new QTreeWidgetItem(it, QStringList(extra.filename));
		}
	}
	m_ui->tw_attachments->resizeColumnToContents(0);
	m_ui->tw_attachments->resizeColumnToContents(1);
}

QPixmap PostEdit::pixmapForFile(const QString& filename)
{
    QPixmap p;
    if(!QPixmapCache::find(filename, &p))
    {
        if(p.load(filename))
			p = p.scaled(QSize(64, 64), Qt::KeepAspectRatio, Qt::SmoothTransformation); // FIXME hardcoded
        QPixmapCache::insert(filename, p);
    }
    return p;
}

void PostEdit::showFeaturesDialog()
{
	FeaturesDialog dlg(this);
	dlg.exec();
	fillAutoAttachments();
}

